不管在ARC还是MRC中,都遵循下面两个基本原则
You own any object you create You create an object using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy”.
MRC
MRC的基本原则
alloc、new
OC中,在alloc、new、retain、copy、mutableCopy一个对象后
或者CF中,在Create(包含Create的函数)、CFRetain、Copy(包含Copy的函数)一个对象后
这个对象的引用记数会加1,由于这个引用记数是在当你加的,所以你有责任在不需要它的时后对它进行release操作。
这与总的基本原则也是相符的,你负责你自己用alloc, init, new, copy & mutableCopy创建的对象的生命周期。
非 alloc、new
如果不是用alloc, init, new, copy & mutableCopy创建的,这个对象就不需要由你来负责,MRC下一种实现方式如下:
/*
* MRC,此段代码摘抄自[《Objective-C 高级编程》](https://book.douban.com/subject/24720270/)
*/
- (id)object
{
id obj = [[NSObject alloc] init];
/*
* 自己持有对象
*/
[obj autorelease];
/*
* 取得对象的存在,但自己不持有对象
*/
return obj;
}
如上,这个对象由autorelease pool来负责释放,不需要创建他的人负责释放。
MRC的AutoreleasePool
AutoreleasePool就是一个池子,给OC对象发送autorelease消息(CF对象对应的是CFAutorelease函数)后就把对象放到池子中,然后在池子结束的地方,对池子中的每一个对象都发送一次release消息。
ARC
ARC简单的理解就是编译器自动在代码合适的位置加入retain和release代码,背后没那么简单,这里也不深究了,ARC只支持OC对象。
ARC中,不能对OC对象进行retain、release、autorelease操作。
在没有手加Autorelease Pool的情况下,Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop
在ARC中,虽然不可以使用retain、release、autorelease,但是可以使用
@autoreleasepool{
...
}
这样可以在进行大的内存操作时,手动的进行一些内存管理。
alloc、new 与非 alloc、new 构建的对象
在 ARC 中,使用 alloc、new 开头的方法名生成的对象不会加入到 Autorelease 池中
在 ARC 中,使用非 alloc、new 开头的方法名生成的对象会自动加入到 Autorelease 池中
这就意味着使用 alloc、new 开头的方法名生成的对象,一旦没有引用时,就会被释放。
而非 alloc、new 开头的方法名生成的对象,即使没有引用时,也不会马上被释放,而是在Autorelease 池中被释放。
如下代码:
+ (NSString *)newHelloWorldString {
return [[NSString alloc] initWithCString:"HelloWorld" encoding:NSUTF8StringEncoding];
}
+ (NSString *)helloWorldString {
return [[NSString alloc] initWithCString:"HelloWorld" encoding:NSUTF8StringEncoding];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
__weak NSString *helloWorldString = [XSQObject helloWorldString];
__weak NSString *newHelloWorldString = [XSQObject newHelloWorldString];
//此处有warning:
//assigning retained object to weak variable;
//object will be released after assignment
NSLog(@"%@", helloWorldString);//输出HelloWorld
NSLog(@"%@", newHelloWorldString);//输出null
}
return 0;
}
ARC 下的指针修饰__weak
、__strong
、__autoreleasing
不管是 ARC 还是 MRC,判断对象是否释放的原则都是看对象是否被强引用。
默认的指针是 strong 类型,强引用一个对象,如果这个对象没有被强引用是,立即释放。
__weak
修饰的指针是弱引用指针,如果 alloc 一个对象,用__weak
指针指向,这个对象一生成就立马被释放了。
__autoreleasing
修饰的指针可以理解为是弱引用这个对象的,但是把这个对象加入自动释放池,这样即使不引用这个对象时,也不会被立即释放。
具体可以看下面的代码说明:
//没有引用的对象,一初始化就立即被释放
[[NSObject alloc] init];
//weak引用刚初始化的对象,一初始化就立即被释放
__weak NSObject *w = [[NSObject alloc] init];
//默认的指针是__strong类型的,持有的对象不会被释放,当对象没有被持有时,则立即被释放
NSObject *a = [[NSObject alloc] init];
a = nil;
//a 之前指向的对象,在程序执行到这里时已经被释放
//__strong类型的,持有的对象不会被释放,当对象没有被持有时,则立即被释放
__strong NSObject *c = [[NSObject alloc] init];
c = nil;
//c 之前指向的对象,在程序执行到这里时已经被释放
//__autoreleasing类型的,会将对象加入自动释放池
__autoreleasing NSObject *b = [[NSObject alloc] init];
b = nil;
//b 之前指向的对象,在程序执行到这里时还未被释放,现在该对象被自动释放池持有
函数返回问题
这里只记录返回CF对象的情况,因为OC对象有ARC,应该也很少会遇到这个问题了,我也没去研究。
如下的函数
//OC
+ (CGColorSpaceRef)getAColorSpace {
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
return colorSpace;
}
//CF
CGColorSpaceRef getAColorSpace(){
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
return colorSpace;
}
如果我们运行Analyze,编辑器会给我们一个Potential leak of an object stored into 'colorSpace'的提示。
原因就是我们调用了CF的Create函数生成了一个CF对象,当前代码片段不需要时又没有release它。
那可怎么办,总不能Create完又release,然后返回个空吧?
有三种办法避免这个
第一种是修改函数名,可以改成如下的名字
//OC + (CGColorSpaceRef)allocAColorSpace {...} //CF CGColorSpaceRef CreateAColorSpace(){...}
这样,名字中包含alloc、Create,说明调用这个方法的代码持有这个方法返回的对象,对象的释放应该由外面的代码来管理。
OC中包含new、alloc、copy的方法名
CF中包含Create、Copy的方法名
都代表返回的对象调用这个方法的代码来管理,如最开始所说的,我们再不需要这个实例时在调用CFRelease来释放。
第二种是给colorSpace添加autorelease,像下面这样
+ (CGColorSpaceRef)getAColorSpace { CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); CFAutorelease(colorSpace); return colorSpace; } CGColorSpaceRef getAColorSpace(){ CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); CFAutorelease(colorSpace); return colorSpace; }
这样,就把colorSpace放入autorelease pool,把这个对象管理的权利交给autorelease pool,colorSpace不会马上释放,外面的代码也能用到它。
第三种是将CF对象Toll-Free转换为OC对象,由ARC管理内存
方法命名与释放
另外,ARC中,创建实例的方法的命名会影响创建的对象的释放
新建实例的方法由alloc, init, new, copy & mutableCopy开头命名
对象有两种释放方式:
- 没用引用了就直接释放
- 调用Autorelease,由Autorelease pool释放
不同的方法前缀会有不同的效果,
详情可参见 ARC and releasing object created in method
Toll-Free
总结:
- CF转化为OC时,并且对象的所有者发生改变,则使用
CFBridgingRelease()
或__bridge_transfer
。- OC转化为CF时,并且对象的所有者发生改变,则使用
CFBridgingRetain()
或__bridge_retained
当一个类型转化到另一种类型时,但是对象所有者没有发生改变,则使用__bridge.
http://www.beyondabel.com/blog/2014/03/05/mrc-arc/
如
CFArrayRef array;
...
NSArray *nArray = (__bridge_transfer NSArray *) array; //对象的所有者发生改变NSArray在ARC下会自动释放
//CFRelease(array); //这里不需要CFRelease了
与MRC混用时的疑惑
2017年07月26日记:
今天MRC与ARC混用,有些疑惑,MRC方法中初始化的实例,作为返回值传到ARC中,在ARC中需要怎么处理。
然后做了个实验
在MRC中新建TestMRCARC类,该类重载dealloc方法并输出log
然后在MRC随便某个类中添加如下方法
+ (TestMRCARC *)makeTestMRCARC {
TestMRCARC *s = [[TestMRCARC alloc] init];
// [s autorelease];
return s;
}
在ARC中调用该方法:
- (IBAction)btnClick:(id)sender {
TestMRCARC *s1 = [XXX makeTestMRCARC];
}
发现s1并没有被释放。
原因是MRC中的方法命名采用的是make开头,所以ARC中不会对s1进行管理。
解决方法:
- 将方法名改成newTestMRCARC,s1就会得到正确的释放
- 或将
[s autorelease]
取消注释,由ARC中的Autorelease pool来释放
另外,在ARC中,即使方法没用按照规则命名,不使用alloc、new、retain、copy、mutableCopy,变量也会得到正确的释放。但是我们再编码时,最好还是按照基本规则来命名。